#include "EditorRender.h"

EditorRender::EditorRender()
    : mpEngine(0),
      mpLight(0)
{
    mpEngine = ne::Engine::GetSingletonPtr();
    if (!mpEngine)
    {
        mpEngine = new ne::Engine();
    }

    createGLSLProgram();

    ne::ModelManager *pModelManager = mpEngine->getModelManager();
    ne::ModelPtr pModel = pModelManager->create("TestModel", "TestModel.model", true);

    ne::MeshManager *pMeshManager = mpEngine->getMeshManager();
    ne::MeshPtr pMesh = pMeshManager->create("TestMesh", "../model/newCharactor/newCharactor.mesh");
    pMesh->load();
    pModel->createSkin("TestSkin", pMesh);

    /*
    ne::SkeletonManager *pSkeletonManager = mpEngine->getSkeletonManager();
    ne::SkeletonPtr pSkeleton = pSkeletonManager->create("TestSkeleton", "../model/newCharactor/newCharactor.skeleton");
    pSkeleton->load();
    pModel->setSkeleton(pSkeleton);
    */
}

EditorRender::~EditorRender()
{
}

void EditorRender::createGLSLProgram()
{
    // Create Bump shader
    ne::GLSLManager *pManager = mpEngine->getGLSLManager();
    ne::GLSLProgramPtr pProgram = pManager->create("Bump", "../shader/bump.glpg", true);
    ne::GLSLShaderPtr pVShader = pProgram->createShaderFromFile("BumpVertexShader", "../shader/bump.glvs", ne::SHADER_TYPE_VERTEX);
    ne::GLSLShaderPtr pFShader = pProgram->createShaderFromFile("BumpFragmentShader", "../shader/bump.glfs", ne::SHADER_TYPE_FRAGMENT);
    pProgram->attachShader(pVShader);
    pProgram->attachShader(pFShader);
    pProgram->link();
}
void EditorRender::render(const ne::Viewport *pViewport)
{
    renderBlock(pViewport);
    renderModels(pViewport);
}

void EditorRender::renderModels(const ne::Viewport *pViewport)
{
    ne::ModelManager *pModelManager = mpEngine->getModelManager();
    ne::ModelPtr pModel = pModelManager->getByName("TestModel");
    renderModel(pViewport, pModel);
}

void EditorRender::renderModel(const ne::Viewport *pViewport, const ne::ModelPtr &pModel)
{
    if (pModel.notNull())// && pModel->isLoaded())
    {
        glPushMatrix();
        glMultMatrixf(pModel->getTransformMatrix().ptr());

        ne::Matrix4 defaultMatrices[128];
        ne::GLSLProgramPtr pProgram = mpEngine->getGLSLManager()->getByName("Bump");
        if (pProgram.isNull())// || !pProgram->isLoaded())
        {
            return;
        }

        pProgram->useProgram();

        if (mpLight)
        {
            pProgram->setUniform("lightPos", mpLight->getPosition());
            pProgram->setUniform("lightDir", mpLight->getDirection());
        }
        else
        {
            pProgram->setUniform("lightPos", pViewport->getCamera()->getPosition());
            pProgram->setUniform("lightDir", pViewport->getCamera()->getDirection());
        }
        pProgram->setUniform("cameraPos", pViewport->getCamera()->getPosition());
        pProgram->setUniform("oriMap", 0);
        pProgram->setUniform("bumpMap", 1);
        int tangentIndex = pProgram->getAttribLocation("neTangent");
        int blendWeightIndex = pProgram->getAttribLocation("neBlendWeights");
        int blendJointIndex = pProgram->getAttribLocation("neBlendJoints");

        const ne::SkinMap &skinMap = pModel->getSkinMap();
        ne::SkinMapConstIterator it, iend;
        iend = skinMap.end();
        for (it=skinMap.begin(); it!=iend; ++it)
        {
            ne::Skin *pSkin = it->second;
            if (pSkin)
            {
                ne::MeshPtr pMesh = pSkin->getMesh();
                if (pMesh.notNull())// && pMesh->isLoaded())
                {
                    // Set texture map
                    ne::TexturePtr pTexture = pMesh->getTexture();
                    if (pTexture.notNull())// && pTexture->isLoaded())
                    {
                        glActiveTexture(GL_TEXTURE0);
                        pTexture->createRenderTexture();
                        glBindTexture(pTexture->getType(), pTexture->getSource());
                        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
                        glEnable(pTexture->getType());
                    }

                    // Set bump map
                    ne::TexturePtr pBumpMap = pMesh->getBumpMap();
                    if (pBumpMap.notNull())// && pBumpMap->isLoaded())
                    {
                        glActiveTexture(GL_TEXTURE1);
                        pBumpMap->createRenderTexture();
                        glBindTexture(pBumpMap->getType(), pBumpMap->getSource());
                        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
                        glEnable(pBumpMap->getType());
                    }

                    // Set skeleton transform matrices
                    ne::SkeletonPtr pSkeleton = pModel->getSkeleton();
                    if (pSkeleton.notNull() && pSkeleton->getNumJoints())// && pSkeleton->isLoaded())
                    {
                        ne::Matrix4Vector matrices;
                        pSkeleton->getAllJointsOffsetTransformMatrices(matrices);
                        pProgram->setUniformArray("neJointOffsetMatrices", matrices.size(), &matrices[0]);
                    }
                    else
                    {
                        pProgram->setUniformArray("neJointOffsetMatrices", 128, &defaultMatrices[0]);
                    }

                    // Render submeshs
                    const ne::SubMeshMap &subMeshMap = pMesh->getChildrenMap();
                    ne::SubMeshMapConstIterator iMesh, iMeshEnd;
                    iMeshEnd = subMeshMap.end();
                    for (iMesh=subMeshMap.begin(); iMesh!=iMeshEnd; ++iMesh)
                    {
                        ne::SubMeshPtr pSubMesh = iMesh->second;
                        if (pSubMesh.isNull())
                        {
                            continue;
                        }
                        //ne::MaterialPtr pMaterial = pSubMesh->getMaterial();
                        //pEngine->getRenderSystem()->setMaterial(pMaterial);

                        ne::VertexDataPtr vertexData = pSubMesh->getVertexData();
                        if (vertexData.isNull())
                        {
                            continue;
                        }

                        ne::VertexArrayPtr va;
                        va = vertexData->getVertexArray(ne::VAS_POSITION);
                        if (va.notNull())
                        {
                            glBindBuffer(GL_ARRAY_BUFFER, va->getSource());
                            glVertexPointer(va->getTypeCount(), va->getGLDataType(), 0, va->getOffsetPointer());
                            glEnableClientState(GL_VERTEX_ARRAY);
                        }

                        va = vertexData->getVertexArray(ne::VAS_NORMAL);
                        if (va.notNull())
                        {
                            glBindBuffer(GL_ARRAY_BUFFER, va->getSource());
                            glNormalPointer(va->getGLDataType(), 0, va->getOffsetPointer());
                            glEnableClientState(GL_NORMAL_ARRAY);
                        }

                        va = vertexData->getVertexArray(ne::VAS_TANGENT);
                        if (va.notNull())
                        {
                            glBindBuffer(GL_ARRAY_BUFFER, va->getSource());
                            glVertexAttribPointer(tangentIndex, va->getTypeCount(), va->getGLDataType(), GL_TRUE, 0, va->getOffsetPointer());
                            glEnableVertexAttribArray(tangentIndex);
                        }

                        va = vertexData->getVertexArray(ne::VAS_BLEND_WEIGHT);
                        if (va.notNull())
                        {
                            glBindBuffer(GL_ARRAY_BUFFER, va->getSource());
                            glVertexAttribPointer(blendWeightIndex, va->getTypeCount(), va->getGLDataType(), GL_FALSE, 0, va->getOffsetPointer());
                            glEnableVertexAttribArray(blendWeightIndex);
                        }

                        va = vertexData->getVertexArray(ne::VAS_BLEND_INDEX);
                        if (va.notNull())
                        {
                            glBindBuffer(GL_ARRAY_BUFFER, va->getSource());
                            glVertexAttribPointer(blendJointIndex, va->getTypeCount(), va->getGLDataType(), GL_FALSE, 0, va->getOffsetPointer());
                            glEnableVertexAttribArray(blendJointIndex);
                        }

                        va = vertexData->getVertexArray(ne::VAS_TEXTURE_COORD);
                        if (va.notNull())
                        {
                            glBindBuffer(GL_ARRAY_BUFFER, va->getSource());
                            glClientActiveTexture(GL_TEXTURE0);
                            glTexCoordPointer(va->getTypeCount(), va->getGLDataType(), 0, va->getOffsetPointer());
                            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
                        }

                        va = vertexData->getVertexArray(ne::VAS_INDEX);
                        if (va.notNull())
                        {
                            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, va->getSource());
                            glDrawElements(vertexData->getGeometryType(), va->getCount(), va->getGLDataType(), va->getOffsetPointer());
                        }

                        glDisableClientState(GL_VERTEX_ARRAY);
                        glDisableClientState(GL_NORMAL_ARRAY);
                        glDisableClientState(GL_COLOR_ARRAY);
                        glDisableVertexAttribArray(tangentIndex);
                        glDisableVertexAttribArray(blendWeightIndex);
                        glDisableVertexAttribArray(blendJointIndex);
                        glDisableClientState(GL_TEXTURE_COORD_ARRAY);

                        glBindBuffer(GL_ARRAY_BUFFER, 0);
                        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
                    }

                    if (pTexture.notNull() && pTexture->isLoaded())
                    {
                        glDisable(pTexture->getType());
                        glActiveTexture(GL_TEXTURE0);
                    }
                }
            }
        }
        pProgram->useProgram(false);
        glPopMatrix();
    }
}

void EditorRender::renderBlock(const ne::Viewport *pViewport)
{
    ne::real blockWidth = 1000.0f;
    ne::real blockSize = 10.0f;
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glPushMatrix();
    glBegin(GL_LINES);
    glColor3f(1.0f, 1.0f, 1.0f);
    for (ne::real i=-blockWidth; i<=blockWidth; i+=blockSize)
    {
        glVertex3f(i, 0.0f, -blockWidth);
        glVertex3f(i, 0.0f, blockWidth);
    }
    for (ne::real i=-blockWidth; i<=blockWidth; i+=blockSize)
    {
        glVertex3f(-blockWidth, 0.0f, i);
        glVertex3f(blockWidth, 0.0f, i);
    }
    glEnd();
    glPopMatrix();
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
